package spice

import (
	"context"
	"log"

	"gitee.com/wujianqiang/golibs/pkg/logging"
	pb "github.com/authzed/authzed-go/proto/authzed/api/v1"
	"github.com/authzed/authzed-go/v1"
	"github.com/authzed/grpcutil"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials/insecure"
)

type SpiceConfig struct {
	Host  string `yaml:"host"`
	Token string `yaml:"token"`
}

func (sc SpiceConfig) NewClient() (*SpiceSrv, error) {
	client, err := authzed.NewClient(
		sc.Host,
		grpc.WithTransportCredentials(insecure.NewCredentials()),
		grpcutil.WithInsecureBearerToken(sc.Token),
	)
	if err != nil {
		logging.Logger.Fatalf("unable to initialize client: %s", err)
		return nil, err
	}
	return &SpiceSrv{client: client}, nil
}

type SpiceSrv struct {
	client *authzed.Client
}

type resourceitem struct {
	rsu []*pb.RelationshipUpdate
}
type delresourceitem struct {
	resource *pb.DeleteRelationshipsRequest
	action   *pb.DeleteRelationshipsRequest
}

// func (ri *delresourceitem) To() []*pb.DeleteRelationshipsRequest {
// rus := []*pb.DeleteRelationshipsRequest{}
// if ri.member != nil {
// 	rus = append(rus, &pb.DeleteRelationshipsRequest{
// 		RelationshipFilter: &pb.RelationshipFilter{
// 			ResourceType: "",
// 		},
// 	})
// }
// if ri.membergroup != nil {
// 	rus = append(rus, &pb.RelationshipUpdate{
// 		Operation:    pb.RelationshipUpdate_OPERATION_CREATE,
// 		Relationship: ri.membergroup,
// 	})
// }
// if ri.adminmember != nil {
// 	rus = append(rus, &pb.RelationshipUpdate{
// 		Operation:    pb.RelationshipUpdate_OPERATION_CREATE,
// 		Relationship: ri.adminmember,
// 	})
// }
// return rus
// }

type Option func(*resourceitem)

func ResourceGroupOption(resource string, groupname string) Option {
	return func(ri *resourceitem) {
		ri.rsu = append(ri.rsu, &pb.RelationshipUpdate{
			Operation: pb.RelationshipUpdate_OPERATION_CREATE,
			Relationship: &pb.Relationship{
				Resource: &pb.ObjectReference{
					ObjectType: "resource",
					ObjectId:   resource,
				},
				Relation: "membergroup",
				Subject: &pb.SubjectReference{
					Object: &pb.ObjectReference{
						ObjectType: "group",
						ObjectId:   groupname,
					},
				},
			},
		})
	}
}

func ResourceAdminOption(resource string, superadmin string) Option {
	return func(ri *resourceitem) {
		ri.rsu = append(ri.rsu, &pb.RelationshipUpdate{
			Operation: pb.RelationshipUpdate_OPERATION_CREATE,
			Relationship: &pb.Relationship{
				Resource: &pb.ObjectReference{
					ObjectType: "resource",
					ObjectId:   resource,
				},
				Relation: "adminmember",
				Subject: &pb.SubjectReference{
					Object: &pb.ObjectReference{
						ObjectType: "superadmin",
						ObjectId:   superadmin,
					},
					OptionalRelation: "member",
				},
			}})
	}
}

func ResourceMemberOption(resource string, username string) Option {
	return func(ri *resourceitem) {
		ri.rsu = append(ri.rsu, &pb.RelationshipUpdate{
			Operation: pb.RelationshipUpdate_OPERATION_CREATE,
			Relationship: &pb.Relationship{
				Resource: &pb.ObjectReference{
					ObjectType: "resource",
					ObjectId:   resource,
				},
				Relation: "member",
				Subject: &pb.SubjectReference{
					Object: &pb.ObjectReference{
						ObjectType: "user",
						ObjectId:   username,
					},
				},
			}})
	}
}

func ActionMemberOption(action string, username string) Option {
	return func(ri *resourceitem) {
		ri.rsu = append(ri.rsu, &pb.RelationshipUpdate{
			Operation: pb.RelationshipUpdate_OPERATION_CREATE,
			Relationship: &pb.Relationship{
				Resource: &pb.ObjectReference{
					ObjectType: "action",
					ObjectId:   action,
				},
				Relation: "member",
				Subject: &pb.SubjectReference{
					Object: &pb.ObjectReference{
						ObjectType: "user",
						ObjectId:   username,
					},
				},
			}})
	}
}
func ActionGroupOption(action string, group string) Option {
	return func(ri *resourceitem) {
		ri.rsu = append(ri.rsu, &pb.RelationshipUpdate{
			Operation: pb.RelationshipUpdate_OPERATION_CREATE,
			Relationship: &pb.Relationship{
				Resource: &pb.ObjectReference{
					ObjectType: "action",
					ObjectId:   action,
				},
				Relation: "member",
				Subject: &pb.SubjectReference{
					Object: &pb.ObjectReference{
						ObjectType: "group",
						ObjectId:   group,
					},
				},
			}})
	}
}

func ActionSuperAdminOption(action string, superadmin string) Option {
	return func(ri *resourceitem) {
		ri.rsu = append(ri.rsu, &pb.RelationshipUpdate{
			Operation: pb.RelationshipUpdate_OPERATION_CREATE,
			Relationship: &pb.Relationship{
				Resource: &pb.ObjectReference{
					ObjectType: "action",
					ObjectId:   action,
				},
				Relation: "adminmember",
				Subject: &pb.SubjectReference{
					Object: &pb.ObjectReference{
						ObjectType: "superadmin",
						ObjectId:   superadmin,
					},
					OptionalRelation: "member",
				},
			}})
	}
}

type DelOption func(*delresourceitem)

func ResourceGroupDelOption(resource string, groupname string) DelOption {
	return func(ri *delresourceitem) {
		ri.resource = &pb.DeleteRelationshipsRequest{
			RelationshipFilter: &pb.RelationshipFilter{
				ResourceType:       "resource",
				OptionalResourceId: resource,
				OptionalRelation:   "membergroup",
				OptionalSubjectFilter: &pb.SubjectFilter{
					SubjectType:       "group",
					OptionalSubjectId: groupname,
				},
			},
		}
	}
}
func ActionGroupDelOption(action string, groupname string) DelOption {
	return func(ri *delresourceitem) {
		ri.action = &pb.DeleteRelationshipsRequest{
			RelationshipFilter: &pb.RelationshipFilter{
				ResourceType:       "action",
				OptionalResourceId: action,
				OptionalRelation:   "member",
				OptionalSubjectFilter: &pb.SubjectFilter{
					SubjectType:       "group",
					OptionalSubjectId: groupname,
				},
			},
		}
	}
}
func (ss *SpiceSrv) CreateSchema() error {
	const Schema = `definition user {}

	definition superadmin {
		relation member: user
	}
	definition group {
		relation member: user
		relation adminmember: user
		permission alluser = member + adminmember
	}
	definition resource {
		relation member: user
		relation membergroup: group
		relation adminmember: superadmin#member
		permission access = member  + membergroup->alluser +adminmember
	}
	definition action {
		relation member: user |group#adminmember
		relation adminmember: superadmin#member
		permission access = member  + adminmember
	}
	`
	request := &pb.WriteSchemaRequest{Schema: Schema}
	_, err := ss.client.WriteSchema(context.Background(), request)
	if err != nil {
		log.Fatalf("failed to write schema: %s", err)
	}
	return err
}
func (ss *SpiceSrv) CreateGroup(groupname string, admin string) error {
	request := &pb.WriteRelationshipsRequest{
		Updates: []*pb.RelationshipUpdate{
			{
				Operation: pb.RelationshipUpdate_OPERATION_CREATE,
				Relationship: &pb.Relationship{
					Resource: &pb.ObjectReference{
						ObjectType: "group",
						ObjectId:   groupname,
					},
					Relation: "adminmember",
					Subject: &pb.SubjectReference{
						Object: &pb.ObjectReference{
							ObjectType: "user",
							ObjectId:   admin,
						},
					},
				},
			},
		},
	}
	_, er := ss.client.WriteRelationships(context.Background(), request)
	return er
}

func (ss *SpiceSrv) AddGroupMember(groupname string, username string) error {
	request := &pb.WriteRelationshipsRequest{
		Updates: []*pb.RelationshipUpdate{
			{
				Operation: pb.RelationshipUpdate_OPERATION_CREATE,
				Relationship: &pb.Relationship{
					Resource: &pb.ObjectReference{
						ObjectType: "group",
						ObjectId:   groupname,
					},
					Relation: "member",
					Subject: &pb.SubjectReference{
						Object: &pb.ObjectReference{
							ObjectType: "user",
							ObjectId:   username,
						},
					},
				},
			},
		},
	}
	_, er := ss.client.WriteRelationships(context.Background(), request)
	return er
}

func (ss *SpiceSrv) DelGroupMember(groupname string, username string) error {
	res := &pb.DeleteRelationshipsRequest{
		RelationshipFilter: &pb.RelationshipFilter{
			ResourceType:       "group",
			OptionalResourceId: groupname,
			OptionalRelation:   "member",
			OptionalSubjectFilter: &pb.SubjectFilter{
				SubjectType:       "user",
				OptionalSubjectId: username,
			},
		},
	}

	_, er := ss.client.DeleteRelationships(context.Background(), res)
	return er
}

func (ss *SpiceSrv) DelGroup(groupname string) error {
	res := &pb.DeleteRelationshipsRequest{
		RelationshipFilter: &pb.RelationshipFilter{
			ResourceType:       "group",
			OptionalResourceId: groupname,
		},
	}

	_, er := ss.client.DeleteRelationships(context.Background(), res)
	return er
}

func (ss *SpiceSrv) CreateSuperAdmin(superadmin string, username string) error {
	request := &pb.WriteRelationshipsRequest{
		Updates: []*pb.RelationshipUpdate{
			{
				Operation: pb.RelationshipUpdate_OPERATION_CREATE,
				Relationship: &pb.Relationship{
					Resource: &pb.ObjectReference{
						ObjectType: "superadmin",
						ObjectId:   superadmin,
					},
					Relation: "member",
					Subject: &pb.SubjectReference{
						Object: &pb.ObjectReference{
							ObjectType: "user",
							ObjectId:   username,
						},
					},
				},
			},
		},
	}
	_, er := ss.client.WriteRelationships(context.Background(), request)
	return er
}

func (ss *SpiceSrv) CheckSuperAdmin(superadmin string, username string) error {
	redclient, er := ss.client.ReadRelationships(context.Background(), &pb.ReadRelationshipsRequest{
		RelationshipFilter: &pb.RelationshipFilter{
			ResourceType:       "superadmin",
			OptionalResourceId: superadmin,
			OptionalRelation:   "member",
			OptionalSubjectFilter: &pb.SubjectFilter{
				SubjectType:       "user",
				OptionalSubjectId: username,
			},
		},
	})
	if er != nil {
		return er
	}
	_, er = redclient.Recv()
	return er

}
func (ss *SpiceSrv) CheckGroupAdmin(groupname string, username string) error {
	redclient, er := ss.client.ReadRelationships(context.Background(), &pb.ReadRelationshipsRequest{
		RelationshipFilter: &pb.RelationshipFilter{
			ResourceType:       "group",
			OptionalResourceId: groupname,
			OptionalRelation:   "adminmember",
			OptionalSubjectFilter: &pb.SubjectFilter{
				SubjectType:       "user",
				OptionalSubjectId: username,
			},
		},
	})
	if er != nil {
		return er
	}
	_, er = redclient.Recv()
	return er
}
func (ss *SpiceSrv) GetGroupAdmin(groupname string) (string, error) {
	redclient, er := ss.client.ReadRelationships(context.Background(), &pb.ReadRelationshipsRequest{
		RelationshipFilter: &pb.RelationshipFilter{
			ResourceType:       "group",
			OptionalResourceId: groupname,
			OptionalRelation:   "adminmember",
		},
	})
	if er != nil {
		return "", er
	}
	admin, er := redclient.Recv()
	if er != nil {
		return "", er
	}
	return admin.Relationship.Subject.Object.ObjectId, er
}

func (ss *SpiceSrv) ListGroupMember(groupname string) ([]string, error) {
	redclient, er := ss.client.ReadRelationships(context.Background(), &pb.ReadRelationshipsRequest{
		RelationshipFilter: &pb.RelationshipFilter{
			ResourceType:       "group",
			OptionalResourceId: groupname,
			OptionalRelation:   "member",
		},
	})
	if er != nil {
		return nil, er
	}
	members := []string{}
	for {
		item, er := redclient.Recv()
		if er != nil {
			break
		}
		members = append(members, item.Relationship.GetSubject().GetObject().ObjectId)
	}
	return members, er
}
func (ss *SpiceSrv) DelSuperAdminMember(superadmin string, username string) error {
	res := &pb.DeleteRelationshipsRequest{
		RelationshipFilter: &pb.RelationshipFilter{
			ResourceType:       "superadmin",
			OptionalResourceId: superadmin,
			OptionalRelation:   "member",
			OptionalSubjectFilter: &pb.SubjectFilter{
				SubjectType:       "user",
				OptionalSubjectId: username,
			},
		},
	}

	_, er := ss.client.DeleteRelationships(context.Background(), res)
	return er
}

func (ss *SpiceSrv) CreateResource(options ...Option) error {
	return ss.handle_create(options...)
}

func (ss *SpiceSrv) handle_create(options ...Option) error {
	var ri resourceitem
	for _, item := range options {
		item(&ri)
	}

	request := &pb.WriteRelationshipsRequest{
		Updates: ri.rsu,
	}
	_, er := ss.client.WriteRelationships(context.Background(), request)
	return er

}
func (ss *SpiceSrv) handle_del(options DelOption) error {
	var ri delresourceitem
	options(&ri)
	if ri.resource != nil {
		_, er := ss.client.DeleteRelationships(context.Background(), ri.resource)
		return er
	}
	if ri.action != nil {
		_, er := ss.client.DeleteRelationships(context.Background(), ri.action)
		return er
	}
	return nil
}

func (ss *SpiceSrv) ListGroupForResource(resourcename string) ([]string, error) {
	redclient, er := ss.client.ReadRelationships(context.Background(), &pb.ReadRelationshipsRequest{
		RelationshipFilter: &pb.RelationshipFilter{
			ResourceType:       "resource",
			OptionalResourceId: resourcename,
			OptionalRelation:   "membergroup",
		},
	})
	if er != nil {
		return nil, er
	}
	groups := []string{}
	for {
		item, er := redclient.Recv()
		if er != nil {
			break
		}
		groups = append(groups, item.Relationship.GetSubject().GetObject().ObjectId)
	}
	return groups, er
}

func (ss *SpiceSrv) DelResource(resource string) error {
	res := &pb.DeleteRelationshipsRequest{
		RelationshipFilter: &pb.RelationshipFilter{
			ResourceType:       "resource",
			OptionalResourceId: resource,
		},
	}
	_, er := ss.client.DeleteRelationships(context.Background(), res)
	return er

}

func (ss *SpiceSrv) CreateAction(options ...Option) error {
	return ss.handle_create(options...)
}

func (ss *SpiceSrv) DelAction(options DelOption) error {
	return ss.handle_del(options)
}
func (ss *SpiceSrv) CheckResource(resource string, user string) (bool, error) {
	res, er := ss.client.CheckPermission(context.Background(), &pb.CheckPermissionRequest{
		Permission: "access",
		Resource: &pb.ObjectReference{
			ObjectType: "resource",
			ObjectId:   resource,
		},
		Subject: &pb.SubjectReference{
			Object: &pb.ObjectReference{
				ObjectType: "user",
				ObjectId:   user,
			},
		},
	})
	if er != nil {
		return false, er
	}
	return res.Permissionship == pb.CheckPermissionResponse_PERMISSIONSHIP_HAS_PERMISSION, nil
}

func (ss *SpiceSrv) CheckAction(action string, user string) (bool, error) {
	res, er := ss.client.CheckPermission(context.Background(), &pb.CheckPermissionRequest{
		Permission: "access",
		Resource: &pb.ObjectReference{
			ObjectType: "action",
			ObjectId:   action,
		},
		Subject: &pb.SubjectReference{
			Object: &pb.ObjectReference{
				ObjectType: "user",
				ObjectId:   user,
			},
		},
	})
	if er != nil {
		return false, er
	}
	return res.Permissionship == pb.CheckPermissionResponse_PERMISSIONSHIP_HAS_PERMISSION, nil
}

func (ss *SpiceSrv) ListGroup() ([]string, error) {
	redclient, er := ss.client.ReadRelationships(context.Background(), &pb.ReadRelationshipsRequest{
		RelationshipFilter: &pb.RelationshipFilter{
			ResourceType: "group",
		},
	})
	if er != nil {
		return nil, er
	}
	groups := []string{}
	tem := map[string]string{}
	for {
		item, er := redclient.Recv()
		if er != nil {
			break
		}
		groupname := item.Relationship.Resource.ObjectId
		if _, ok := tem[groupname]; !ok {
			groups = append(groups, groupname)
			tem[groupname] = "1"
		}

	}
	return groups, er
}

func (ss *SpiceSrv) CheckGroupPermission(groupname string, username string) (bool, error) {
	res, er := ss.client.CheckPermission(context.Background(), &pb.CheckPermissionRequest{
		Permission: "alluser",
		Resource: &pb.ObjectReference{
			ObjectType: "group",
			ObjectId:   groupname,
		},
		Subject: &pb.SubjectReference{
			Object: &pb.ObjectReference{
				ObjectType: "user",
				ObjectId:   username,
			},
		},
	})
	if er != nil {
		return false, er
	}
	return res.Permissionship == pb.CheckPermissionResponse_PERMISSIONSHIP_HAS_PERMISSION, nil
}

// func main() {
// sc := SpiceConfig{
// 	Host:  "10.33.32.239:50051",
// 	Token: "mykkokk",
// }
// client, er := sc.NewClient()
// if er != nil {
// 	logging.Logger.Error(er)
// }
// er = client.CreateSchema()
// logging.Logger.Error(er)
// create group risk

// client.CreateGroup("data", "wjq")
// client.AddGroupMember("data", "wangwei")
// client.AddGroupMember("data", "jz")
// client.AddGroupMember("data", "1111")
// client.AddGroupMember("data", "111")

// client.CreateGroup("donghu", "xiepeng")
// client.AddGroupMember("donghu", "wangzhen")

// client.CreateGroup("xihu", "wensi")
// client.AddGroupMember("xihu", "leifei")

// time.Sleep(1 * time.Second)
// client.CreateResource(ResourceGroupOption("/api", "xihu"))
// time.Sleep(1 * time.Second)
// groups, er := client.ListGroupForResource("/api")
// fmt.Println(groups)
// fmt.Println(er)
// // superadmin platform
//client.CreateSuperAdmin("platform", "wjq")
// member, er := client.ListGroupMember("data")
// logging.Logger.Error(er)
// fmt.Println(member)

// er = client.CreateResource(ResourceAdminOption("/api", "platform"), ResourceGroupOption("/api", "risk"))
// logging.Logger.Error(er)
// res, er := client.CheckResource("/api", "hanzhixiao")
// println(er)
// println(res)
// er = client.DelGroupMember("risk", "hanzhixiao")
// logging.Logger.Error(er)
// res, _ = client.CheckResource("/api", "liusuai")
// println("after del user....")
// println(res)
// client.ListGroup()

// }
